/**
 * 模拟数据工具
 * @author luguoxiang
 * @date 2021-09-07
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 * 例：
 const api = createApi('/system/user', '用户')

 mock.mockApi(api, {
  data: {
    username: (i) => mock.code('4,8'),
    nickname: (i) => mock.name(),
    lastLoginDate: (i) => mock.datetime(),
    userType: (i) => i%3
  },
  defaultValue: {
    avatar: (v) => 'http://localhost:8000/src/assets/app/logo.png',
    username: (v) => v ?? mock.code('4,8'),
    userType: (v) => v ?? 1
  }
 })

 export default {
  ...api
 }
 */

import {
  message,
  nextId,
  randomArray,
  randomBoolean,
  randomCode,
  randomElement,
  randomNumber,
  randomString,
  sort,
  toStringArray,
} from '@/soar';

/**
 * 邮箱地址
 */
const EMAIL_URLS = ['qq.com', '163.com', '139.com'];

/**
 * 省
 */
const PROVINCES = ['广西', '广东', '贵州', '云南', '四川', '湖南', '福建', '河南', '北京', '上海', '天津', '重庆'];

/**
 * 公司
 */
const COMPANY_LX = ['科技', '信息', '信息科技', '服务', '食品', '安保'];
const COMPANY_ZR = ['有限', '股份有限', '责任有限'];
const COMPANY_GM = ['集团', '公司'];

export interface ApiHandler {
  paramsHandle?: Function;
  dataHandle?: Function;
}

type Value<T, R> = R | { (params: T): R };

export interface MockData {
  /**
   * 数据定义，通过函数生成值
   * {
   *     field1(index){ return value },
   *     field2(index){ return value },
   *     field3: value,
   *     ...
   *     fieldn(index){ return value },
   * }
   */
  data: { [propName: string]: Value<number, any> };
  /**
   * 预生成数量：
   *    单个数值：具体数量
   *    有两个数值的数数组：在两个数值之间取随机数
   *    超过两个数值的数组：随机取其中一个
   * 默认：[5,521]
   */
  total?: number | number[];
}

export interface MockConfig extends MockData {
  /**
   * 主键字段，默认id
   */
  idField?: string;
  /**
   * 默认值定义，通过函数生成默认值
   * {
   *     field1(oldValue){ return newValue },
   *     field2(oldValue){ return newValue },
   *     ...
   *     fieldn(oldValue){ return newValue },
   * }
   */
  defaultValue: { [propName: string]: Value<any, any> };
  /**
   * 加载延迟时间，参考total
   * 默认：[30,500]
   */
  timeout?: number | number[];
  /**
   * api 处理器
   */
  apiHandlers?: [string, ApiHandler];
}

function copy(data: any) {
  return JSON.parse(JSON.stringify(data));
}

/**
 * 获取数值值
 * @param value
 */
function randomNumberValue(value: number | number[]) {
  if (typeof value === 'number') {
    return value;
  }
  if (value.length === 2) {
    return randomNumber(value[0], value[1]);
  }
  return randomElement(value);
}

/**
 * 处理默认值
 * @param d
 * @param defaultValueDefine
 * @param idField
 */
function handleDefaultValue(d: any, defaultValueDefine: any, idField: string) {
  for (const key in defaultValueDefine) {
    d[key] ??= defaultValueDefine[key]?.(d[key]);
  }
  if (!d[idField]) {
    d[idField] = nextId();
  }
}

function paramsHandle(handler: ApiHandler, params: any) {
  if (handler?.paramsHandle) {
    return handler.paramsHandle(params);
  }
  return params;
}

function dataHandle(handler: ApiHandler, params: any, data: any): any[] {
  if (handler?.dataHandle) {
    return handler.dataHandle(params, data);
  }
  return data;
}

/**
 * 响应处理
 * @param config
 * @param data
 */
function response(config: any, data: any) {
  return new Promise((resolve) => {
    setTimeout(() => {
      config.log.response = data;
      config.log.loading = false;
      resolve(data);
    }, randomNumberValue(config.timeout ?? [30, 500]));
  });
}

/**
 * 生成模拟数据
 * @param config
 */
export function mockData(config: MockData) {
  const define: { [p: string]: Value<number, any> } = config.data;
  const length = randomNumberValue(config.total ?? [5, 521]);
  const data: any[] = [];
  for (let i = 0; i < length; i++) {
    const d: any = {};
    for (const key in define) {
      const gen = define[key];
      if (typeof gen === 'function') {
        d[key] = gen(i);
      } else {
        d[key] = gen;
      }
    }
    data.push(d);
  }
  return data;
}

/**
 * 包裹api，提供模拟数据支持
 * @param api 业务api对象
 * @param config mock配置
 */
export function mockApi(api: any = {}, config: MockConfig): object {
  // 非开发环境禁用mock
  if (!import.meta.env.DEV) {
    return api;
  }
  // 生成数据
  const defaultValueDefine: { [p: string]: Value<any, any> } = config.defaultValue ?? {};
  const idField = config.idField ?? 'id';
  let $data: any[] = mockData(config);
  for (const d of $data) {
    handleDefaultValue(d, defaultValueDefine, idField);
  }

  // 包裹api
  const $api: any = {
    create(data: any) {
      if (!data) {
        message.error({ message: '缺少数据' });
        return Promise.reject();
      }
      const log = { params: data, loading: true, response: undefined };
      console.log(`mock: ${api.urls.create}`, log);
      data = copy(data);
      handleDefaultValue(data, defaultValueDefine, idField);
      $data.push(data);
      // @ts-ignore
      return response({ log, timeout: config.timeout });
    },
    update(data: any) {
      if (!data) {
        message.error({ message: '缺少数据' });
        return Promise.reject();
      }
      const log = { params: data, loading: true, response: undefined };
      console.log(`mock: ${api.urls.update}`, log);
      const id = data[idField];
      const oldData = $data.find((x: any) => x[idField] === id);
      if (!oldData) {
        message.error({ message: '数据不存在' });
        return Promise.reject();
      }
      data = copy(data);
      for (const k in data) {
        const value = data[k];
        if (value === undefined || value === null) {
          continue;
        }
        oldData[k] = data[k];
      }
      // @ts-ignore
      return response({ log, timeout: config.timeout });
    },
    remove(ids: any | any[]) {
      if (!ids || ids.length === 0) {
        message.error({ message: '请选择要删除的数据' });
        return Promise.reject();
      }
      const log = { params: ids, loading: true, response: undefined };
      console.log(`mock: ${api.urls.remove}`, log);
      if (!Array.isArray(ids)) {
        ids = [ids];
      }
      $data = $data.filter((x) => !ids.includes(x[idField]));
      // @ts-ignore
      return response({ log, timeout: config.timeout });
    },
    detail(id: number | string) {
      if (!id) {
        message.error({ message: '缺少id' });
        return Promise.reject();
      }
      const log = { params: id, loading: true, response: undefined };
      console.log(`mock: ${api.urls.detail}`, log);
      const d = $data.find((x) => x[idField] == id);
      return response({ log, timeout: config.timeout }, copy(d));
    },
    query(params: any = {}) {
      // @ts-ignore
      params = paramsHandle(config.apiHandlers?.query, params);
      const log = { params, loading: true, response: undefined };
      console.log(`mock: ${api.urls.query}`, log);
      const pageNo = params?.pageNo ?? 1;
      const pageSize = params?.pageSize ?? $data.length;
      const begin = (pageNo - 1) * pageSize;
      const end = pageNo * pageSize;

      // @ts-ignore
      let result: any = dataHandle(config.apiHandlers?.query, params, $data);
      // 排序
      if (params?.$soarQuery?.orderBys) {
        sort(result, params.$soarQuery.orderBys);
      }
      // 获取指定字段
      if (params?.$soarQuery?.fields) {
        const fields = toStringArray(params.$soarQuery.fields);
        result = result.map((x) => {
          const d: any = {};
          for (const field of fields) {
            d[field] = x[field];
          }
          return d;
        });
      }
      return response(
        { log, timeout: config.timeout },
        {
          list: result.filter((x: any, i: number) => begin <= i && i < end).map(copy),
          total: result.length,
        },
      );
    },
  };
  for (const key in $api) {
    api[key] = $api[key];
  }
  return api;
}

/**
 * 生成手机号
 * @param source 第二位源字符串
 * @returns {string}
 */
function mobile(source = '3456789') {
  return `1${randomString(1, source)}${randomString(9, '0123456789')}`;
}

/**
 * 生成邮箱地址
 * @param length 长度
 * @returns {string}
 */
function email(length = '3,16') {
  return `${randomCode(length)}@${randomElement(EMAIL_URLS)}`;
}

/**
 * 生成qq号
 * @returns {string}
 */
function qq(length = '9,11') {
  return randomCode(length, '0123456789');
}

/**
 * 获取生日
 * @param minAge 最小年龄
 * @param maxAge 最大年龄
 * @returns {string}
 */
function birthday(minAge = 0, maxAge = 100) {
  const time = new Date().getTime();
  const birthday = randomNumber(time - maxAge * 31536000000, time - minAge * 31536000000);
  return new Date(birthday);
}

/**
 * 随机生成钱
 * @param min 最小值
 * @param max 最大值
 * @returns {number}
 */
function money(min = 0, max = 10000) {
  return randomNumber(min, max, 2);
}

/**
 * 生成时间数据
 * @param min 最小时间毫秒值
 * @param max 最大时间毫秒值
 * @returns {Date}
 */
function datetime(min = 0, max = new Date().getTime()) {
  return new Date(randomNumber(min, max));
}

/**
 * 生成ip4数据
 * @returns {String}
 */
function ip4() {
  return `${randomNumber(0, 255)}.${randomNumber(0, 255)}.${randomNumber(0, 255)}.${randomNumber(0, 255)}`;
}

/**
 * 生成姓名
 * @param familyNameSource 姓氏源字符串
 * @param nameSource 名字源字符串
 * @returns {string}
 */
function name(familyNameSource = '赵钱孙李周吴郑王卢莫黄', nameSource?: string) {
  if (nameSource) {
    return randomString(1, familyNameSource) + randomString('1,3', nameSource);
  }
  return randomString(1, familyNameSource) + randomString('1,3');
}

/**
 * 生成公司名称
 * @param nameSource 名称源字符串
 * @returns {string}
 */
function company(nameSource?) {
  return (
    randomElement(PROVINCES, '') +
    randomString('4,16', nameSource) +
    randomElement(COMPANY_LX, '') +
    randomElement(COMPANY_ZR, '') +
    randomElement(COMPANY_GM)
  );
}

/**
 * 生成省份数据
 * @returns {*}
 */
function province() {
  return randomElement(PROVINCES);
}

export default {
  mockData,
  mockApi,
  number: randomNumber,
  boolean: randomBoolean,
  code: randomCode,
  string: randomString,
  array: randomArray,
  element: randomElement,
  id: nextId,
  email,
  mobile,
  qq,
  birthday,
  money,
  datetime,
  ip4,
  name,
  company,
  province,
};
